#ifndef SINGLETON_H
#define SINGLETON_H

#include <QSharedPointer>
#include "call-once.h"

// handy macro for setting private singleton functions:
// constructor, destructor, copy constructors and friends
#ifndef SINGLETON_OBJECT
#define SINGLETON_OBJECT(type)                            \
private:                                                  \
    explicit type();                                      \
    ~type();                                              \
    Q_DISABLE_COPY(type)                                  \
    friend class QtSharedPointer::ExternalRefCount<type>; \
    friend struct QtSharedPointer::CustomDeleter<type,QtSharedPointer::NormalDeleter>; \
    friend class Singleton<type>;
#endif // SINGLETON_OBJECT

template <typename type>
class Singleton
{
public:
    static QSharedPointer<type> instance();

protected:
    Singleton() {}
    ~Singleton();
    Q_DISABLE_COPY(Singleton)

    static void init();

    static QSharedPointer<type> m_sharedInstance;
    static QWeakPointer<type> m_weakInstance;
    static QBasicAtomicInt flag;
};

template<typename type> QSharedPointer<type> Singleton<type>::m_sharedInstance;
template<typename type> QWeakPointer<type> Singleton<type>::m_weakInstance;
template<typename type> QBasicAtomicInt Singleton<type>::flag = Q_BASIC_ATOMIC_INITIALIZER(CallOnce::CallOnceEmpty);

template<typename type>
Singleton<type>::~Singleton()
{
    flag.fetchAndStoreOrdered(CallOnce::CallOnceEmpty);
}

template<typename type>
QSharedPointer<type> Singleton<type>::instance()
{
    CallOnce::callOnce(init, flag);
    Q_ASSERT(!m_weakInstance.isNull());

    if (!m_sharedInstance.isNull())
    {
        QSharedPointer<type> instance(m_sharedInstance);
        m_sharedInstance.clear();
        return instance;
    }

    return m_weakInstance.toStrongRef();
}

template<typename type>
void Singleton<type>::init()
{
    Q_ASSERT(m_sharedInstance.isNull());
    Q_ASSERT(m_weakInstance.isNull());

    m_sharedInstance = QSharedPointer<type>(new type);
    m_weakInstance = m_sharedInstance.toWeakRef();
}

#endif // SINGLETON_H
